Feign is a declarative web service client that integrates with Spring Cloud, allowing you to create HTTP clients with annotations. RestTemplate, on the other hand, is a more traditional Spring-based approach for making HTTP requests to remote services. Feign is typically easier to use and less boilerplate-heavy than RestTemplate.
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
In RestTemplate, you can use the getForObject()
or postForObject()
methods to call a REST API. You need to create a RestTemplate bean, which will be used for communication with the API.
RestTemplate restTemplate = new RestTemplate();
String url = "http://example.com/api/users/1";
User user = restTemplate.getForObject(url, User.class);
WebClient is the non-blocking, reactive alternative to RestTemplate. It supports asynchronous communication and allows for a more efficient handling of requests, especially in reactive applications. RestTemplate is blocking and synchronous, while WebClient provides better scalability and performance in modern applications.
WebClient webClient = WebClient.create();
Mono userMono = webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class);
Feign simplifies service-to-service communication by providing declarative HTTP client interfaces. It reduces boilerplate code, integrates well with Spring Cloud, and automatically handles error responses, retries, and load balancing when used with Spring Cloud Netflix (Eureka, Ribbon). It also supports configuration via annotations, making it easier to maintain and modify.
In RestTemplate, you can handle error responses by using an HttpErrorHandler
or catching exceptions such as HttpClientErrorException
and HttpServerErrorException
to capture and process error details.
try {
User user = restTemplate.getForObject(url, User.class);
} catch (HttpClientErrorException e) {
// Handle client-side errors
} catch (HttpServerErrorException e) {
// Handle server-side errors
}
To create a Feign client in Spring Boot, you need to annotate your interface with @FeignClient
and provide the service name. You can then define the endpoints in the interface using Spring MVC annotations such as @RequestMapping
.
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
WebClient supports asynchronous HTTP requests using Mono
and Flux
types. You can perform an asynchronous request by invoking the retrieve()
method and handling the response asynchronously using subscribe()
.
webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user.getName()));
You can configure timeouts for WebClient by using a custom ClientHttpConnector
that applies connection and response timeouts. This is typically done with ReactorNettyHttpClient
in Spring WebFlux.
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxIdleTime(Duration.ofSeconds(30))
.maxLifeTime(Duration.ofMinutes(5))
.build();
HttpClient httpClient = HttpClient.create(provider)
.responseTimeout(Duration.ofSeconds(10));
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
@RequestMapping
annotation in Feign?
The @RequestMapping
annotation in Feign is used to map a method in the Feign client interface to a REST endpoint. It specifies the HTTP method (GET, POST, PUT, DELETE) and the URL path for the request.
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can configure a custom error handler by implementing the ResponseErrorHandler
interface and setting it on a RestTemplate
instance using setErrorHandler()
.
public class CustomErrorHandler implements ResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR ||
response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// Handle error based on response status
}
}
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new CustomErrorHandler());
WebClient provides several benefits, such as support for non-blocking, reactive programming. It can handle large-scale, concurrent requests with better performance, as it allows for asynchronous communication and is ideal for reactive applications.
You can use Feign with Spring Cloud by combining it with a service discovery tool like Eureka. Feign will automatically resolve service names through the service registry to communicate with other services in the system.
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
Mono
is a reactive type that represents a single or empty asynchronous value. Flux
is used for representing a sequence of asynchronous values. WebClient uses these types to handle single or multiple responses from a service.
Mono userMono = webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class);
Flux usersFlux = webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.bodyToFlux(User.class);
You can enable automatic retries in Feign by using Spring Cloud Netflix's Retryer
class. You can configure the retry policy using the @FeignClient
annotation.
@FeignClient(name = "user-service", configuration = RetryConfig.class)
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Configuration
public class RetryConfig {
@Bean
public Retryer retryer() {
return new Retryer.Default(100, 1000, 3); // Initial, max interval, max attempts
}
}
You can add custom authentication headers to WebClient requests by using the header()
method to set authentication tokens or other headers.
webClient.get()
.uri("http://example.com/api/users/1")
.header("Authorization", "Bearer token_value")
.retrieve()
.bodyToMono(User.class);
WebClient.Builder
in service-to-service communication?
WebClient.Builder
allows you to customize WebClient instances. You can set base URLs, add filters, or configure default headers and codecs before creating the WebClient instance.
WebClient webClient = WebClient.builder()
.baseUrl("http://example.com")
.defaultHeader("Authorization", "Bearer token_value")
.build();
RestTemplate uses HttpMessageConverter
to map JSON responses to Java objects. By default, it uses MappingJackson2HttpMessageConverter
for JSON, which automatically deserializes JSON responses into Java objects.
RestTemplate restTemplate = new RestTemplate();
User user = restTemplate.getForObject("http://example.com/api/users/1", User.class);
Yes, you can perform file uploads with Feign by using the MultipartFile
class in the Feign client interface. You need to use the @RequestPart
annotation to send the file as part of the multipart request.
@FeignClient(name = "file-service")
public interface FileClient {
@RequestMapping(method = RequestMethod.POST, value = "/upload")
String uploadFile(@RequestPart("file") MultipartFile file);
}
To make synchronous calls with WebClient, you can use the block()
method, which blocks the calling thread until the response is received.
Mono userMono = webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class);
User user = userMono.block(); // Blocks the thread until the response is received
RestTemplate does not support asynchronous communication out of the box. However, you can wrap the RestTemplate
calls in a Future
or CompletableFuture
to achieve asynchronous behavior.
ExecutorService executor = Executors.newFixedThreadPool(1);
Callable task = () -> restTemplate.getForObject("http://example.com/api/users/1", User.class);
Future future = executor.submit(task);
User user = future.get();
WebClient supports asynchronous communication out of the box by returning Mono
or Flux
, which represent asynchronous results.
Mono userMono = webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class);
// To trigger the call asynchronously
userMono.subscribe(user -> System.out.println(user.getName()));
You can configure timeouts in WebClient using the baseUrl()
and timeout()
methods, or by customizing the underlying HttpClient
configuration.
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().responseTimeout(Duration.ofSeconds(5))))
.baseUrl("http://example.com")
.build();
WebClient does not have built-in retry functionality, but you can use Retry
from the reactor-extra
library to add retries.
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.retry(3))) // Retry 3 times
.baseUrl("http://example.com")
.build();
Synchronous communication blocks the calling thread until the response is received, while asynchronous communication returns control to the calling thread immediately and processes the response when it is available.
// Synchronous
User user = webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class)
.block();
// Asynchronous
webClient.get()
.uri("http://example.com/api/users/1")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user.getName()));
You can use placeholders in the URI and bind them using uri()
method with uriVariables
.
webClient.get()
.uri("http://example.com/api/users/{id}/details", 1)
.retrieve()
.bodyToMono(User.class);
In Feign, you can use a request interceptor to add authentication headers to your requests.
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> requestTemplate.header("Authorization", "Bearer token_value");
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can configure timeouts in Feign by setting connectTimeout
and readTimeout
in the Feign.Builder
.
@Bean
public Request.Options options() {
return new Request.Options(5000, TimeUnit.MILLISECONDS, 5000, TimeUnit.MILLISECONDS, true);
}
Feign allows you to log requests and responses using a Logger
implementation.
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
The default logging level in Feign is NONE
, meaning it does not log requests or responses unless explicitly configured.
You can enable retries in Feign by configuring a retryer in the Feign client. The default retryer retries up to 5 times with an increasing delay.
@FeignClient(name = "user-service", retryer = Retryer.NEVER_RETRY)
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can configure a custom error decoder to handle errors in Feign.
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 404) {
return new CustomNotFoundException("Not Found");
}
return new Exception("Generic error");
}
}
WebClient can be used for sending a POST request by specifying the request body and calling the post()
method.
webClient.post()
.uri("http://example.com/api/users")
.bodyValue(new User("John", "Doe"))
.retrieve()
.bodyToMono(User.class)
.subscribe(response -> System.out.println(response.getName()));
In WebClient, you can add request headers by using the header()
method.
webClient.get()
.uri("http://example.com/api/users")
.header("Authorization", "Bearer token")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user.getName()));
The retrieve()
method in WebClient is used to start the actual HTTP request. It returns a ResponseSpec
, which is used to specify how to handle the response (e.g., mapping it to a Java object).
webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user.getName()));
You can use Spring Cloud Feign with Eureka by enabling Feign clients in your Spring Boot application and specifying the service name to interact with.
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient("user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
In WebClient, you can log request/response headers by using a custom ExchangeFilterFunction
that logs the headers.
ExchangeFilterFunction logRequest = ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
System.out.println("Request: " + clientRequest.headers());
return Mono.just(clientRequest);
});
ExchangeFilterFunction logResponse = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
System.out.println("Response: " + clientResponse.headers());
return Mono.just(clientResponse);
});
WebClient webClient = WebClient.builder()
.filter(logRequest)
.filter(logResponse)
.baseUrl("http://example.com")
.build();
In WebClient, you can specify the content type of the request by using the contentType()
method.
webClient.post()
.uri("http://example.com/api/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(new User("John", "Doe"))
.retrieve()
.bodyToMono(User.class)
.subscribe(response -> System.out.println(response.getName()));
You can handle different response types in Feign by specifying the return type and using Spring’s ResponseEntity
for more control over the response.
@FeignClient("user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
ResponseEntity getUser(@PathVariable("id") String id);
}
In Spring Cloud, Feign and Ribbon work together for client-side load balancing. Feign will interact with Eureka for service discovery, and Ribbon will handle the load balancing.
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
In RestTemplate, you can make a GET request by using the getForObject()
method.
RestTemplate restTemplate = new RestTemplate();
String url = "http://example.com/api/users/{id}";
User user = restTemplate.getForObject(url, User.class, "123");
You can send JSON data in a POST request using RestTemplate by using the postForObject()
method and setting the body.
RestTemplate restTemplate = new RestTemplate();
String url = "http://example.com/api/users";
User user = new User("John", "Doe");
User createdUser = restTemplate.postForObject(url, user, User.class);
You can configure WebClient with a base URL using the baseUrl()
method while building the WebClient instance.
WebClient webClient = WebClient.builder()
.baseUrl("http://example.com")
.build();
You can configure timeouts for RestTemplate by setting a custom ClientHttpRequestFactory
with a timeout configuration.
int timeout = 5000; // 5 seconds
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.build();
HttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
Feign allows you to set a custom error decoder to handle specific error conditions. You can create your custom decoder and configure it using the errorDecoder()
bean.
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 404) {
return new NotFoundException("Resource not found");
}
return new Exception("Generic error");
}
}
The exchange()
method in WebClient allows for more control over the response, including access to the full ClientResponse
.
webClient.get()
.uri("http://example.com/api/users")
.exchange()
.flatMap(response -> response.bodyToMono(User.class))
.subscribe(user -> System.out.println(user.getName()));
RestTemplate can throw different exceptions like HttpClientErrorException
or HttpServerErrorException
. You can handle these exceptions using a try-catch block.
try {
User user = restTemplate.getForObject(url, User.class);
} catch (HttpClientErrorException e) {
// handle client error
} catch (HttpServerErrorException e) {
// handle server error
} catch (Exception e) {
// handle generic error
}
The main difference is that Feign is declarative and allows you to define client interfaces for HTTP calls, while RestTemplate is a more imperative way of making HTTP requests programmatically.
@FeignClient("user-service")
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can configure timeout settings in WebClient by using a HttpClient
with custom timeout configurations.
Duration timeout = Duration.ofSeconds(5);
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.responseTimeout(timeout)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
))
.baseUrl("http://example.com")
.build();
You can make asynchronous calls with WebClient by using the subscribe()
method or returning a Mono
or Flux
.
webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user.getName()));
To create a Feign client interface, you annotate the interface with @FeignClient
and define the methods for API calls.
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can make a PUT request using WebClient by calling put()
and passing the body of the request.
WebClient webClient = WebClient.create();
webClient.put()
.uri("http://example.com/api/users/{id}", 123)
.bodyValue(updatedUser)
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
Feign allows you to set a custom logger by specifying a Logger.Level
in the @FeignClient
annotation or configuration.
@FeignClient(name = "user-service", configuration = CustomFeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Configuration
public class CustomFeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
In WebClient, you can handle HTTP errors by using the onStatus()
method to check for specific response statuses.
webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomClientException("Client error")))
.onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new CustomServerException("Server error")))
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
To perform basic authentication with RestTemplate, you need to set the Authorization
header using HttpHeaders
.
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("username", "password");
HttpEntity entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
String url = "http://example.com/api/users";
ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, User.class);
You can log requests and responses in Feign by configuring the Logger
class and setting the Logger.Level
.
Logger.Level feignLoggerLevel = Logger.Level.FULL;
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@RequestMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
You can pass query parameters in WebClient by using the uri()
method with UriComponentsBuilder
or by directly specifying the parameters in the URI string.
WebClient webClient = WebClient.create();
webClient.get()
.uri("http://example.com/api/users?age={age}&city={city}", 30, "New York")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
You can send a JSON payload with a PUT request by creating an HttpEntity
with the body and sending it using exchange()
.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity<>(updatedUser, headers);
RestTemplate restTemplate = new RestTemplate();
String url = "http://example.com/api/users/{id}";
restTemplate.exchange(url, HttpMethod.PUT, entity, User.class, "123");
Feign provides a declarative way to make HTTP requests, which is more concise and easier to maintain compared to RestTemplate's imperative approach.
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
WebClient calls are asynchronous by default, but you can block the thread to perform a synchronous call by using block()
.
User user = webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.bodyToMono(User.class)
.block();
To implement retry logic with WebClient, you can use retryWhen()
and specify the retry conditions.
webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.bodyToMono(User.class)
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(1)))
.subscribe(user -> System.out.println(user));
You can configure timeouts for RestTemplate using HttpClient
and setting the connection and read timeouts.
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000) // 5 seconds
.setSocketTimeout(5000) // 5 seconds
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
To send a POST request with a JSON payload using Feign, you annotate the method with @PostMapping
and use RequestBody
for the data.
@FeignClient("user-service")
public interface UserClient {
@PostMapping("/users")
User createUser(@RequestBody User user);
}
You can enable logging for RestTemplate by using a RequestLoggingInterceptor
and adding it to the RestTemplate
.
RequestLoggingInterceptor loggingInterceptor = new RequestLoggingInterceptor();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(loggingInterceptor);
WebClient allows you to handle different content types by using the accept()
method to specify the expected content type for the response.
webClient.get()
.uri("http://example.com/api/users")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
To use a custom error decoder with Feign, you create a class that implements ErrorDecoder
and provide it in the Feign configuration.
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 404) {
return new NotFoundException("Resource not found");
}
return new Exception("Generic error");
}
}
@FeignClient(name = "user-service", configuration = CustomFeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Configuration
public class CustomFeignConfig {
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
To use RestTemplate with OAuth2 authentication, you need to set the Authorization
header with the access token.
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
HttpEntity entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity response = restTemplate.exchange("http://example.com/api/resource", HttpMethod.GET, entity, String.class);
Feign supports retry functionality with a Retryer
bean that can be configured to control the retry behavior.
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, 1000, 3);
}
@FeignClient(name = "user-service", configuration = RetryConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Configuration
public class RetryConfig {
@Bean
public Retryer retryer() {
return new Retryer.Default(100, 1000, 3); // retry 3 times with exponential backoff
}
}
To handle exceptions globally with RestTemplate, you can define a ResponseErrorHandler
and set it on your RestTemplate instance.
public class CustomErrorHandler implements ResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR
|| response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR;
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// handle the error
}
}
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new CustomErrorHandler());
You can authenticate with API keys in WebClient by adding the API key to the Authorization
or custom header.
webClient.get()
.uri("http://example.com/api/users")
.header("Api-Key", "your-api-key")
.retrieve()
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
You can log requests and responses in WebClient by using filter()
to capture the details of the request and response.
WebClient webClient = WebClient.builder()
.filter((request, next) -> {
System.out.println("Request: " + request);
return next.exchange(request).doOnTerminate(() -> System.out.println("Response: " + request));
})
.baseUrl("http://example.com")
.build();
You can configure a WebClient instance to handle custom error responses by using the onStatus()
method to check for the status and then handle the error.
WebClient webClient = WebClient.create("http://example.com");
webClient.get()
.uri("/users")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomClientException("Client Error")))
.onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new CustomServerException("Server Error")))
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
You can use RestTemplate
to perform a GET request and handle the response as a list of objects using ParameterizedTypeReference
.
RestTemplate restTemplate = new RestTemplate();
ResponseEntity> response = restTemplate.exchange(
"http://example.com/api/users",
HttpMethod.GET,
null,
new ParameterizedTypeReference>() {}
);
List users = response.getBody();
You can handle multipart file uploads with RestTemplate by creating a MultipartBodyBuilder
and sending the request as a multipart/form-data
request.
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", new FileSystemResource("path/to/file"));
builder.part("metadata", "Some metadata");
RestTemplate restTemplate = new RestTemplate();
HttpEntity> entity = new HttpEntity<>(builder.build());
ResponseEntity> entity = new HttpEntity<>(builder.build());
ResponseEntity> entity = new HttpEntity<>(builder.build());
ResponseEntity> entity = new HttpEntity<>(builder.build());
ResponseEntity> entity = new HttpEntity<>(builder.build());
ResponseEntity response = restTemplate.exchange(
"http://example.com/upload",
HttpMethod.POST,
entity,
String.class
);
To configure Feign to use a custom encoder, you can provide a Encoder
bean in the Feign configuration.
@Configuration
public class FeignConfig {
@Bean
public Encoder feignEncoder() {
return new JacksonEncoder();
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@PostMapping("/users")
User createUser(@RequestBody User user);
}
You can extract custom header values from the response in WebClient by using the headers()
method to access the response headers.
webClient.get()
.uri("http://example.com/api/users")
.retrieve()
.toEntity(User.class)
.doOnSuccess(response -> {
String customHeader = response.getHeaders().getFirst("X-Custom-Header");
System.out.println("Custom Header: " + customHeader);
})
.subscribe(user -> System.out.println(user));
To send a PUT request using Feign, you use the @PutMapping
annotation on the method, passing the request body as a parameter.
@FeignClient("user-service")
public interface UserClient {
@PutMapping("/users/{id}")
User updateUser(@PathVariable("id") String id, @RequestBody User user);
}
You can configure WebClient to follow redirects by setting the followRedirects()
method in the WebClient builder.
WebClient webClient = WebClient.builder()
.baseUrl("http://example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.filter((request, next) -> next.exchange(request))
.build();
To handle API rate limiting in WebClient, you can use retryWhen()
to add retries with delays or use a custom filter to intercept rate-limiting responses.
webClient.get()
.uri("http://example.com/api/endpoint")
.retrieve()
.onStatus(HttpStatus.TOO_MANY_REQUESTS::equals, response -> Mono.error(new RateLimitExceededException("Rate limit exceeded")))
.bodyToMono(String.class)
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)))
.subscribe(response -> System.out.println(response));
To use a custom decoder in Feign, you can create a Decoder
bean and provide it in the Feign configuration.
@Configuration
public class FeignConfig {
@Bean
public Decoder feignDecoder() {
return new CustomDecoder();
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can configure WebClient to handle retries by using the retryWhen()
method in the request pipeline.
webClient.get()
.uri("http://example.com/api/resource")
.retrieve()
.bodyToMono(String.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2)))
.subscribe(response -> System.out.println(response));
You can configure a custom retry mechanism for Feign by defining a Retryer
bean in the Feign configuration.
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, TimeUnit.MILLISECONDS.toMillis(1), 3);
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can perform a POST request using RestTemplate and handle the response body by passing the entity to the exchange()
method.
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity<>(new User("John", "Doe"), headers);
ResponseEntity response = restTemplate.exchange(
"http://example.com/api/users",
HttpMethod.POST,
entity,
User.class
);
User createdUser = response.getBody();
You can set the timeout values for WebClient using the baseUrl
method and applying a custom exchangeStrategies()
.
WebClient webClient = WebClient.builder()
.baseUrl("http://example.com")
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().responseTimeout(Duration.ofSeconds(10))
))
.build();
You can handle invalid responses by using the onStatus()
method to inspect the HTTP status code and perform custom error handling.
WebClient webClient = WebClient.create("http://example.com");
webClient.get()
.uri("/api/users")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomClientException("Client Error")))
.onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new CustomServerException("Server Error")))
.bodyToMono(User.class)
.subscribe(user -> System.out.println(user));
You can authenticate using OAuth2 by setting the OAuth2AuthorizedClientManager
in WebClient.
OAuth2AuthorizedClientManager authorizedClientManager =
new OAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
WebClient webClient = WebClient.builder()
.baseUrl("http://example.com")
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.build();
You can perform a PUT request with WebClient by using the put()
method and passing the request body.
WebClient webClient = WebClient.create("http://example.com");
webClient.put()
.uri("/api/users/{id}", 1)
.bodyValue(new User("Jane", "Doe"))
.retrieve()
.bodyToMono(Void.class)
.subscribe();
You can set up Feign with a custom error decoder by creating a bean of type ErrorDecoder
in the configuration.
@Configuration
public class FeignConfig {
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can handle cookies in WebClient by accessing them from the CookieHandler
or by using a custom filter.
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.create().cookieHandler(new CookieManager())))
.build();
RestTemplate is blocking by default, but you can perform asynchronous requests using AsyncRestTemplate
.
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
ListenableFuture> future = asyncRestTemplate.exchange(
"http://example.com/api/users",
HttpMethod.GET,
null,
User.class
);
future.addCallback(result -> System.out.println(result.getBody()), ex -> ex.printStackTrace());
You can create a Feign client with basic authentication by using RequestInterceptor
to add an authorization header.
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> requestTemplate.header(HttpHeaders.AUTHORIZATION, "Basic " + encodedCredentials);
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can define timeouts for Feign clients by configuring okhttp3
with custom timeouts.
@Configuration
public class FeignConfig {
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can add a custom filter to WebClient using the filter()
method to modify the request and response.
WebClient webClient = WebClient.builder()
.filter((request, next) -> {
System.out.println("Request URI: " + request.uri());
return next.exchange(request);
})
.baseUrl("http://example.com")
.build();
You can create a fallback mechanism in Feign by using the fallback
attribute in the Feign client declaration.
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
@Component
public class UserClientFallback implements UserClient {
@Override
public User getUser(String id) {
return new User("Fallback", "User");
}
}
You can add headers to every WebClient request by using the defaultHeader()
method.
WebClient webClient = WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.baseUrl("http://example.com")
.build();
webClient.get()
.uri("/api/users")
.retrieve()
.bodyToMono(User.class)
.subscribe();
You can implement service discovery with Feign and Spring Cloud by using @EnableFeignClients
and @EnableDiscoveryClient
annotations, and setting up an application that integrates with Eureka or Consul.
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can handle retries with WebClient by using the retry
operator from Project Reactor.
webClient.get()
.uri("/api/users")
.retrieve()
.bodyToMono(User.class)
.retry(3) // Retries 3 times before failing
.subscribe(user -> System.out.println(user));
You can log request and response details in WebClient by using the exchangeStrategies()
method or by adding a custom logging filter.
WebClient webClient = WebClient.builder()
.filter((request, next) -> {
System.out.println("Request URI: " + request.uri());
return next.exchange(request).doOnTerminate(() -> System.out.println("Request completed"));
})
.baseUrl("http://example.com")
.build();
You can implement OAuth2 token management in Feign by using RequestInterceptor
to attach the token to every request.
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor oauthRequestInterceptor() {
return requestTemplate -> requestTemplate.header(HttpHeaders.AUTHORIZATION, "Bearer " + getOAuth2Token());
}
}
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") String id);
}
You can validate input data by using custom logic or annotations before making a request with Feign.
@FeignClient(name = "user-service")
public interface UserClient {
@PostMapping("/users")
void createUser(@Valid @RequestBody User user);
}
You can configure a timeout for RestTemplate by using a SimpleClientHttpRequestFactory
with custom timeouts.
int timeout = 5000;
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(timeout);
factory.setReadTimeout(timeout);
RestTemplate restTemplate = new RestTemplate(factory);